/**
 * 
 */
package ikelib;

import java.util.LinkedList;
import java.util.TreeSet;

/**
 * フィルターのベースとなるクラス、ストリームとしてデータを与えることができる。
 * キューに入力して、出力が計算できるようなれば、順次キューに出力する。
 * 
 * @author ikechan
 *
 */
abstract class FilterPipe {
	private LinkedList<Double> instream = null;
	private LinkedList<Double> outstream = null;
	private int forward=0;
	private int backward=0;
	private int count=0;
	/**
	 * フィルターするデータの幅を返す
	 * @return
	 */
	public int getWidth(){
		return 1 + forward + backward;
	}
	/**
	 * 
	 * @param w フィルタするデータの幅、１＝フィルタしない、２＝n-1,nの２つのデータを用いて計算する。３＝n-1,n,n+1を用いて計算する。
 	 */
	public FilterPipe(int w){
		count = 0;
		if(w>1){
			w--;
			if(w % 2 == 0){ // 自分を含めて奇数のときは、前後同じ数
				forward  = w / 2;
				backward = w / 2;
			}
			else{	// 自分を含めて偶数のときは、前を１つ多く取る（幅が２の場合は、自分と１つ前）
				forward  = w / 2;
				backward = w / 2 + 1;
			}
		}
		instream = new LinkedList<Double>();
		outstream = new LinkedList<Double>();
	};
	/**
	 * データを入れながらフィルター処理する。入力データ数と出力データ数が同じになるように、端は調整する。
	 * 最初は必要なデータが溜まるまでoutputしない。また、最終データをinputした後は、flushして
	 * outputできるようにすること。
	 * @param d
	 */
	public void input(double d){
		instream.add(d);
		count++;
		LinkedList<Double> ts = new LinkedList<Double>();
		if(count<=forward){
			// たまるのをまつ
		}
		else{
			Double[] dt = instream.toArray(new Double[1]);
			int max = 1 + forward + backward; // 計算するために必要最低限のデータ
			if(count < max){
				max = count;
			}
			else{
				count--;
				instream.removeFirst();
			}
			for(int i=0;i<max;i++){
				ts.add(dt[i]);
			}
			double dd = calcFilter(ts);
			outstream.add(dd);
		}
	}
	/**
	 * 入力バッファをフラッシュし、出力へ書き出す。
	 * 入力データを入れ終えたら、これを呼び出し後データを取り出す。
	 * @return　出力したデータ数を返す。
	 */
	public int flush(){
		LinkedList<Double> ts = new LinkedList<Double>();
		Double[] dt = instream.toArray(new Double[1]);
		for(int k=0;k<count;k++){
			for(int i=k;i<count;i++){
				ts.add(dt[i]);
			}
			double dd = calcFilter(ts);
 			outstream.add(dd);
		}
		return count;
	}
	/**
	 * データを１つとりだす。
	 * @return
	 */
	public Double output(){
		double ret = outstream.getFirst();
		outstream.removeFirst();
		return ret;
	}
	/**
	 * 出力可能なデータ数を返す
	 * @return
	 */
	public int available(){
		int ret = outstream.size();
		return ret;
	}
	
	
	/**
	 * 入力データをすべて与えてすべてのフィルターを計算する。（互換性のため、widthの指定が異なる。）<BR>
	 * LinkedList<Double> 　in: 入力データのリスト<BR>
 	 *   データの端では、メジアンをとるデータの数をカットすることにする。<BR>
	 *   つまり、　width=1,データ長4のとき、<BR>
	 *   0 : median(0,1)<BR>
	 *   1 : median(0,1,2)<BR>
	 *   2 : median(1,2,3)<BR>
	 *   3 : median(2,3)<BR>
	 *   のようになる。<BR>
	 * 
	 * @param in LinkedList<Double>入力データ
	 * @param width　メジアン計算のためのデータ幅 （自分を含まず前方後方のそれぞれの長さ、１のとき前後１つつまり３つのデータのメジアン）
	 * @return　LinkedList<Double> 計算結果 入力データと同じ数
 	 */
	public  LinkedList<Double> calc(LinkedList<Double> in){
		
		Double[] dt = in.toArray(new Double[1]);
		return calc(dt);
	}
	/**
	 * 入力データをすべて与えてすべてのフィルターを計算する。（互換性のため、widthの指定が異なる。）<BR>
	 * Double[] in: 入力データのリスト<BR>
 	 *   データの端では、メジアンをとるデータの数をカットすることにする。<BR>
	 *   つまり、　width=1,データ長4のとき、<BR>
	 *   0 : median(0,1)<BR>
	 *   1 : median(0,1,2)<BR>
	 *   2 : median(1,2,3)<BR>
	 *   3 : median(2,3)<BR>
	 *   のようになる。<BR>
	 * 
	 * @param in double[] 入力データ
	 * @param width　メジアン計算のためのデータ幅 （自分を含まず前方後方のそれぞれの長さ、１のとき前後１つつまり３つのデータのメジアン）
	 * @return　LinkedList<Double> 計算結果 入力データと同じ数
 	 */
	public  LinkedList<Double> calc(Double[] in){
		
		int width = forward + backward + 1;
		Double[] dt = in;
		
		LinkedList<Double> ret = new LinkedList<Double>();
		
		if(dt.length > width*2){
			LinkedList<Double> ts = new LinkedList<Double>();
			for(int k=0;k<dt.length;k++){
				ts.clear();
				if(k<backward){
					for(int i=0;i<k+forward+1;i++){
						ts.add(dt[i]);
					}
				}
				else if((k+forward) > (dt.length-1)){
					for(int i=k-backward;i<dt.length;i++){
						ts.add(dt[i]);
					}
				}
				else{
					for(int i=k-backward;i<k+forward+1;i++){
						ts.add(dt[i]);
					}
				}
				double dat = calcFilter(ts);
				//double dat = 0.0;
				ret.add(dat);
			}
		}
		else{
			//ret = in;	// 
		}
		return ret;
	}
	/**
	 * データを1/wにデシメートする
	 * @param in　
	 * @return　LinkedList<Double> で返す。
	 */
	public  LinkedList<Double> decimate(Double[] in){
		
		int width = forward + backward + 1;
		Double[] dt = in;
		
		LinkedList<Double> ret = new LinkedList<Double>();
		
		LinkedList<Double> ts = new LinkedList<Double>();
		for(int k=0;k<dt.length;k++){
			ts.add(dt[k]);
			if((k % width) == width-1){
				double dat = calcFilter(ts);
				ret.add(dat);
				ts.clear();
			}
		}
		return ret;
	}

	
	/**
	 * これをオーバーライドすること
	 * @param ts　LinkedList<Double> ts フィルタの入力となるデータ <BR>
	 * @return 　 double 計算結果 <BR>
	 */
	abstract   double calcFilter(LinkedList<Double> ts);
//	{
//		int len = ts.size();
//		double ret = 0.0;
//		if(len>0) ret = ts.first();
//		return ret;
//	}
}
